package cn.com.anypay.manager.service.impl;

import cn.com.anypay.manager.common.utils.AnyJsonUtils;
import cn.com.anypay.manager.miaoma.config.Config;
import cn.com.anypay.manager.miaoma.config.ConfigService;
import cn.com.anypay.manager.service.OssService;
import cn.com.anysdk.oss.api.IOssConfig;
import cn.com.anysdk.oss.api.IOssService;
import cn.com.anysdk.oss.api.UploadOptions;
import cn.com.anysdk.oss.config.AliyunOssConfig;
import cn.com.anysdk.oss.config.LocalFileSystemConfig;
import cn.com.anysdk.oss.config.MinioConfig;
import cn.com.anysdk.oss.config.TencentCloudOssConfig;
import cn.com.anysdk.oss.exception.OssException;
import cn.com.anysdk.oss.impl.OssServiceManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class OssServiceImpl implements OssService {

    private static final String SERVICE_TYPE = "OSS";
    @Resource
    private OssServiceManager manager;

    @Resource
    private ConfigService configService;

    @Value("${anysdk.oss.local.base-path:/tmp/uploads}")
    private String localBasePath;

    private IOssConfig currentConfig;
    private IOssService currentOssService;

    @PostConstruct
    public void init() {
        try {
            // 确保本地文件系统目录存在
            ensureDirectoryExists(localBasePath);

            // 使用默认配置初始化，不从数据库读取
            log.info("OSS服务使用默认配置初始化，本地存储路径: {}", localBasePath);
            LocalFileSystemConfig config = new LocalFileSystemConfig();
            config.setBasePath(localBasePath);
            currentConfig = config;
            currentOssService = manager.switchProvider(currentConfig);
            log.info("OSS服务初始化成功，类型: {}", currentConfig.getProvider());
        } catch (Exception e) {
            log.error("OSS服务初始化失败: {}", e.getMessage(), e);
            // 初始化失败时使用默认配置
            try {
                ensureDirectoryExists(localBasePath);
                LocalFileSystemConfig config = new LocalFileSystemConfig();
                config.setBasePath(localBasePath);
                currentConfig = config;
                currentOssService = manager.switchProvider(currentConfig);
            } catch (Exception ex) {
                log.error("使用默认配置初始化OSS服务也失败: {}", ex.getMessage(), ex);
            }
        }
    }

    @Override
    public String switchProvider(Map<String, Object> config) {
        String provider = (String) config.get("type");
        if (provider == null) {
            throw new OssException("Provider is required");
        }

        IOssConfig ossConfig = createConfigFromMap(config);
        IOssService ossService = manager.switchProvider(ossConfig);

        this.currentConfig = ossConfig;
        this.currentOssService = ossService;

        // 保存配置到数据库
        Config serviceConfig = configService.saveConfig(SERVICE_TYPE, provider, config);
        log.debug("保存OSS配置成功: {}", serviceConfig);

        return "OSS Provider switched to: " + ossConfig.getProvider();
    }

    @Override
    public IOssService getCurrentService() {
        return currentOssService;
    }

    @Override
    public Map<String, Object> uploadFile(MultipartFile file, String path, boolean isPublic) {
        // 基础验证
        if (file == null || file.isEmpty()) {
            throw new OssException("请选择要上传的文件");
        }

        // 文件大小验证（限制为50MB）
        long maxSize = 50 * 1024 * 1024; // 50MB
        if (file.getSize() > maxSize) {
            throw new OssException("文件大小超过限制，最大支持50MB");
        }

        // 文件名验证
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.trim().isEmpty()) {
            throw new OssException("文件名不能为空");
        }

        // 检查文件扩展名 - 采用黑名单方式，阻止危险文件类型
        String[] blockedExtensions = {
            // 可执行文件
            ".exe", ".bat", ".cmd", ".com", ".scr", ".msi", ".dll", ".sys",
            // 脚本文件（可能包含恶意代码）
            ".vbs", ".vbe", ".jse", ".wsf", ".wsh", ".ps1", ".psm1", ".psd1",
            // 系统文件
            ".reg", ".inf", ".cpl", ".scf", ".lnk", ".url",
            // 其他危险文件
            ".jar", ".class", ".dex", ".apk", ".ipa", ".dmg", ".pkg",
            // 宏文件（可能包含恶意宏）
            ".xlsm", ".xltm", ".docm", ".dotm", ".pptm", ".potm", ".ppam", ".ppsm", ".sldm"
        };
        boolean isBlockedType = false;
        String lowerFilename = originalFilename.toLowerCase();
        for (String ext : blockedExtensions) {
            if (lowerFilename.endsWith(ext)) {
                isBlockedType = true;
                break;
            }
        }
        if (isBlockedType) {
            throw new OssException("禁止上传的文件类型，出于安全考虑，不允许上传可执行文件、脚本文件或包含宏的文档");
        }

        log.info("开始上传文件: {}, 大小: {} bytes", originalFilename, file.getSize());

        try {
            // 创建临时文件
            Path tempPath = Files.createTempFile("upload-", file.getOriginalFilename());
            file.transferTo(tempPath.toFile());

            // 设置上传选项
            UploadOptions options = isPublic ? UploadOptions.publicFile() : UploadOptions.privateFile(3600L);

            // 构建对象键，避免路径重复
            String objectKey;
            if (path != null && !path.isEmpty()) {
                // 清理路径，移除开头的斜杠和uploads前缀
                String cleanPath = path.replaceFirst("^/+", "").replaceFirst("^uploads/+", "");
                objectKey = "uploads/" + cleanPath + "/" + file.getOriginalFilename();
            } else {
                objectKey = "uploads/" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/")) + file.getOriginalFilename();
            }

            log.debug("构建的文件路径: {}", objectKey);

            // 获取当前OSS服务并上传文件
            IOssService service = getCurrentService();
            if (service == null) {
                throw new OssException("OSS服务未初始化，请先配置存储服务");
            }

            String url = service.uploadFile(objectKey, tempPath.toFile(), options);

            // 清理临时文件
            Files.delete(tempPath);

            // 构建返回结果
            Map<String,Object> result = new HashMap<>();
            result.put("url", url);
            result.put("path", objectKey);
            result.put("name", file.getOriginalFilename());
            result.put("size", file.getSize());
            result.put("type", file.getContentType());
            result.put("isPublic", isPublic);
            result.put("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            result.put("storageType", service.getConfig().getProvider());

            return result;

        } catch (IOException e) {
            log.error("文件IO操作失败，文件名: {}, 错误: {}", file.getOriginalFilename(), e.getMessage(), e);
            throw new OssException("文件读写失败，请检查文件是否损坏或权限不足", e);
        } catch (OssException e) {
            // 重新抛出OSS相关异常，保持原始错误信息
            throw e;
        } catch (Exception e) {
            log.error("文件上传意外错误，文件名: {}, 错误: {}", file.getOriginalFilename(), e.getMessage(), e);
            throw new OssException("文件上传失败，请稍后重试或联系管理员", e);
        }
    }

    @Override
    public Map<String, Object> deleteFile(String path) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 获取当前OSS服务
            IOssService service = getCurrentService();
            if (service == null) {
                throw new OssException("OSS服务未初始化，请先配置存储服务");
            }

            // 检查文件是否存在
            if (!service.doesObjectExist(path)) {
                // 如果是本地存储，尝试查找带UUID前缀的文件
                if ("LocalFileSystem".equals(service.getConfig().getProvider())) {
                    log.debug("删除文件时，文件不存在，尝试查找带UUID前缀的文件: {}", path);
                    String uuidPath = findFileWithUuidPrefix(path);
                    if (uuidPath != null) {
                        log.debug("删除时找到带UUID前缀的文件: {}", uuidPath);
                        path = uuidPath;
                    } else {
                        result.put("success", false);
                        result.put("message", "文件不存在");
                        return result;
                    }
                } else {
                    result.put("success", false);
                    result.put("message", "文件不存在");
                    return result;
                }
            }

            // 删除文件
            service.deleteFile(path);

            result.put("success", true);
            result.put("message", "文件删除成功");
            result.put("path", path);
            return result;
        } catch (OssException e) {
            throw e;
        } catch (Exception e) {
            throw new OssException("文件删除失败: " + e.getMessage(), e);
        }
    }

    @Override
    public Map<String, Object> getCurrentConfig() throws OssException {
        try {
            // 如果当前配置为空，尝试从数据库加载
            if (currentConfig == null) {
                Config serviceConfig = configService.getConfig(SERVICE_TYPE, null);
                if (serviceConfig != null) {
                    Map<String, Object> configMap = AnyJsonUtils.parseMap(serviceConfig.getConfigJson());
                    currentConfig = createConfigFromMap(configMap);
                    currentOssService = manager.switchProvider(currentConfig);
                    return configMap;
                }
                // 没有找到配置时返回null，而不是抛出异常
                return null;
            }

            // 如果当前配置是默认的LocalFileSystem配置，返回null表示没有配置
            if ("LocalFileSystem".equals(currentConfig.getProvider())) {
                return null;
            }

            Config serviceConfig = configService.getConfig(SERVICE_TYPE, currentConfig.getProvider());
            if (serviceConfig == null) {
                // 没有找到特定provider的配置时返回null
                return null;
            }
            return AnyJsonUtils.parseMap(serviceConfig.getConfigJson());
        } catch (Exception e) {
            log.error("获取OSS配置失败: {}", e.getMessage(), e);
            throw new OssException("Failed to get OSS configuration", e);
        }
    }

    private IOssConfig createConfigFromMap(Map<String, Object> map) {
        String provider = (String) map.get("type");
        if (provider == null) {
            throw new OssException("Missing provider type");
        }
        return switch (provider.toLowerCase()) {
            case "aliyun" -> {
                String endpoint = (String) map.get("endpoint");
                String accessKeyId = (String) map.get("accessKeyId");
                String accessKeySecret = (String) map.get("accessKeySecret");
                String bucket = (String) map.get("bucket");
                if (endpoint == null || endpoint.isEmpty() ||
                    accessKeyId == null || accessKeyId.isEmpty() ||
                    accessKeySecret == null || accessKeySecret.isEmpty() ||
                    bucket == null || bucket.isEmpty()) {
                    throw new OssException("OSS配置缺少必要参数");
                }
                yield AliyunOssConfig.builder()
                        .endpoint(endpoint)
                        .accessKeyId(accessKeyId)
                        .accessKeySecret(accessKeySecret)
                        .bucketName(bucket)
                        .isPrivate(false)
                        .build();
            }
            case "tencent" -> {
                String endpoint = (String) map.get("endpoint");
                String accessKeyId = (String) map.get("accessKeyId");
                String accessKeySecret = (String) map.get("accessKeySecret");
                String bucket = (String) map.get("bucket");
                if (endpoint == null || endpoint.isEmpty() ||
                    accessKeyId == null || accessKeyId.isEmpty() ||
                    accessKeySecret == null || accessKeySecret.isEmpty() ||
                    bucket == null || bucket.isEmpty()) {
                    throw new OssException("OSS配置缺少必要参数");
                }
                yield TencentCloudOssConfig.builder()
                        .endpoint(endpoint)
                        .accessKeyId(accessKeyId)
                        .accessKeySecret(accessKeySecret)
                        .bucketName(bucket)
                        .isPrivate(false)
                        .build();
            }
            case "minio" -> {
                String endpoint = (String) map.get("endpoint");
                String accessKeyId = (String) map.get("accessKeyId");
                String accessKeySecret = (String) map.get("accessKeySecret");
                String bucket = (String) map.get("bucket");
                if (endpoint == null || endpoint.isEmpty() ||
                    accessKeyId == null || accessKeyId.isEmpty() ||
                    accessKeySecret == null || accessKeySecret.isEmpty() ||
                    bucket == null || bucket.isEmpty()) {
                    throw new OssException("OSS配置缺少必要参数");
                }
                yield MinioConfig.builder()
                        .endpoint(endpoint)
                        .accessKeyId(accessKeyId)
                        .accessKeySecret(accessKeySecret)
                        .bucketName(bucket)
                        .isPrivate(false)
                        .build();
            }
            case "local" -> {
                String basePath = (String) map.getOrDefault("basePath", localBasePath);
                ensureDirectoryExists(basePath);
                LocalFileSystemConfig config = new LocalFileSystemConfig();
                config.setBasePath(basePath);
                yield config;
            }
            default -> throw new OssException("Unsupported provider: " + provider);
        };
    }

    @Override
    public Map<String, Object> getConfigByProvider(String provider) {
        try {
            log.info("根据提供商获取OSS配置: {}", provider);
            Config serviceConfig = configService.getConfig(SERVICE_TYPE, provider);
            if (serviceConfig == null) {
                log.info("数据库中未找到{}类型的OSS配置", provider);
                return null;
            }
            Map<String, Object> configMap = AnyJsonUtils.parseMap(serviceConfig.getConfigJson());
            log.info("成功获取{}类型的OSS配置", provider);
            return configMap;
        } catch (Exception e) {
            log.error("根据提供商获取OSS配置失败: {}", e.getMessage(), e);
            // 返回null而不是抛出异常，让前端能够正常处理
            return null;
        }
    }

    @Override
    public byte[] downloadFile(String path) {
        try {
            // 获取当前OSS服务
            IOssService service = getCurrentService();
            if (service == null) {
                throw new OssException("OSS服务未初始化，请先配置存储服务");
            }

            // 检查文件是否存在
            if (!service.doesObjectExist(path)) {
                // 如果是本地存储，尝试查找带UUID前缀的文件
                if ("LocalFileSystem".equals(service.getConfig().getProvider())) {
                    log.debug("文件不存在，尝试查找带UUID前缀的文件: {}", path);
                    String uuidPath = findFileWithUuidPrefix(path);
                    if (uuidPath != null) {
                        log.debug("找到带UUID前缀的文件: {}", uuidPath);
                        path = uuidPath;
                    } else {
                        throw new OssException("文件不存在: " + path);
                    }
                } else {
                    throw new OssException("文件不存在: " + path);
                }
            }

            // 下载文件并转换为字节数组
            try (java.io.InputStream inputStream = service.downloadFile(path)) {
                byte[] fileContent = inputStream.readAllBytes();
                log.debug("文件下载成功: {}, 大小: {} bytes", path, fileContent.length);
                return fileContent;
            }

        } catch (OssException e) {
            throw e;
        } catch (Exception e) {
            log.error("文件下载失败: path={}, error={}", path, e.getMessage(), e);
            throw new OssException("文件下载失败: " + e.getMessage(), e);
        }
    }

    /**
     * 查找带UUID前缀的文件
     * @param originalPath 原始路径（不包含UUID）
     * @return 带UUID的完整路径，如果找不到返回null
     */
    private String findFileWithUuidPrefix(String originalPath) {
        try {
            // 构建完整的文件系统路径
            Path fullPath = Paths.get(localBasePath, originalPath);
            Path parentDir = fullPath.getParent();
            String fileName = fullPath.getFileName().toString();
            
            if (parentDir == null || !Files.exists(parentDir)) {
                log.warn("父目录不存在: {}", parentDir);
                return null;
            }
            
            // 在父目录中查找包含UUID前缀的文件
            try (var stream = Files.list(parentDir)) {
                String targetFileName = stream
                    .filter(Files::isRegularFile)
                    .map(Path::getFileName)
                    .map(Path::toString)
                    .filter(name -> {
                        // 查找格式为 UUID_原文件名 的文件
                        // 例如: a6b88e1add014f698adb3b205873e078_斗拱.svg
                        if (name.contains("_")) {
                            String nameAfterUuid = name.substring(name.indexOf("_") + 1);
                            return nameAfterUuid.equals(fileName);
                        }
                        return false;
                    })
                    .findFirst()
                    .orElse(null);
                
                if (targetFileName != null) {
                    // 构建新的完整路径（相对于uploads目录）
                    Path originalParent = Paths.get(originalPath).getParent();
                    if (originalParent != null) {
                        Path relativePath = originalParent.resolve(targetFileName);
                        String newPath = relativePath.toString().replace('\\', '/');
                        log.debug("找到UUID文件: {} -> {}", originalPath, newPath);
                        return newPath;
                    } else {
                        // 如果原始路径没有父目录，直接返回文件名
                        log.debug("找到UUID文件: {} -> {}", originalPath, targetFileName);
                        return targetFileName;
                    }
                }
            }
            
            log.warn("未找到带UUID前缀的文件: {}", originalPath);
            return null;
            
        } catch (Exception e) {
            log.error("查找UUID文件时发生错误: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 确保目录存在，如果不存在则创建
     */
    private void ensureDirectoryExists(String dirPath) {
        try {
            Path path = Paths.get(dirPath);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
                log.info("创建本地存储目录: {}", dirPath);
            }
        } catch (IOException e) {
            log.error("创建本地存储目录失败: {}", dirPath, e);
            throw new OssException("Failed to create local storage directory: " + dirPath, e);
        }
    }
}